#ifndef __ABSTRACT_ADVENTURE_MAP_H__
#define __ABSTRACT_ADVENTURE_MAP_H__

/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 abstract_adventure_map.h

	$Header: /heroes4/abstract_adventure_map.h $

	$NoKeywords: $

 ************************************************************************/
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// don't warn about exception code, which we may not use.
#pragma warning( disable : 4530 )
#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

#include <cassert>
#include <map>
#include <string>
#include <vector>

#include "abstract_tile.h"
#include "adventure_tile_size.h"
#include "copy_on_write_ptr.h"
#include "isometric_map.h"
#include "quad.h"
#include "shared_ptr.h"
#include "terrain.h"
#include "vector_set.h"
#include "visibility_point.h"
#include "visibility_set.h"

// terms:
// 
// screen size is an arbitrary rectangle.
// screen coordinates are expressed in X, Y values.
// (0,0) in screen coordinates is the upper left corner.
// (width, height) in screen coorinates is the lower right corner
//
// maps are always square.
// for the adventure map, the view size, in screen coordinates, is:
// view size, in screen coordinates, is (tile width * map width) / 2 wide by
// (tile height * map height) / 2 high.
//
// for the combat map, the logical map size, for a given screen size, is:
// (view port width / tile width) + (view port height / tile height)
//
//
// map coordinates are expressed in Row and Column values.
// coordinates are expressed (Column, Row)
// (0,0)           in map coordinates is off the top edge of the screen, centered.
// (size, 0)      in map coordinates is off the right edge of the screen, centered.
// (0, size)     in map coordinates is off the left edge of the screen, centered.
// (size, size) in map coordinates is off the bottom edge of the screen, centered.
// +1 Row results in -2 X, +1 Y.
// +1 Column results in +2 X, +1 Y.
//
// directions run from 0 to 7.  0 is up -1 Row, 2 is +1 Column, 4 is +1 Row, 6 is -1 Column

// ---------------------------------------------------------------
// Forward declarations
// ---------------------------------------------------------------
enum t_difficulty;
class t_abstract_adv_object;
class t_abstract_adventure_map;
class t_shroud_transition_map;

// ---------------------------------------------------------------
// A type for identifying a particular adventure object on a map
// ---------------------------------------------------------------

typedef int t_adventure_map_object_id;
t_adventure_map_object_id const k_invalid_adventure_map_object_id = -1;

// -------------------------------------------------------------------
// abstract map cell, used primarily by graphics engine, derived classes
// created by game and engine
// -------------------------------------------------------------------
class t_abstract_adventure_tile : public t_abstract_tile
{
public:
	// Types
	typedef t_adventure_map_object_id t_object_id;

	// Member functions
	int			get_intersecting_object_count() const;
	t_object_id	get_intersecting_object_id( int arg ) const;

private:
	// Types
	typedef std::vector< t_object_id >					t_object_id_vector;
	typedef t_copy_on_write_ptr< t_object_id_vector >	t_object_id_vector_ptr;

	// Data members
	t_object_id_vector_ptr	m_object_id_vector_ptr;

	// Member functions
	void add_intersecting_object_id( t_object_id object_id );
	void remove_intersecting_object_id( t_object_id object_id );

	friend class t_abstract_adventure_map;
};

// -------------------------------------------------------------------
// inlines for t_abstract_adventure_tile
// -------------------------------------------------------------------
inline int t_abstract_adventure_tile::get_intersecting_object_count() const
{
	return m_object_id_vector_ptr->size();
}

inline t_abstract_adventure_tile::t_object_id t_abstract_adventure_tile::get_intersecting_object_id( int arg  ) const
{
	assert( arg >= 0 && arg < get_intersecting_object_count() );
	return ( *m_object_id_vector_ptr )[ arg ];
}

// -------------------------------------------------------------------
// t_abstract_adventure_tile_vertex class
// -------------------------------------------------------------------
class t_abstract_adventure_tile_vertex : public t_abstract_tile_vertex
{
};

// -------------------------------------------------------------------
// t_adventure_map_view_cell - An adventure map's viewable area is
// divided into cells for efficient drawing purposes.
// -------------------------------------------------------------------

int const k_adventure_map_view_cell_size = 32;
int const k_adventure_map_view_cell_shift = 5;

class t_adventure_map_view_cell
{
public:
	// Types
	typedef t_adventure_map_object_id t_object_id;

	struct t_object_subimage_info
	{
		t_object_id		object_id;
		int				subimage_num;

		t_object_subimage_info() {}
		t_object_subimage_info( t_object_id new_object_id, int new_subimage_num )
			:	object_id( new_object_id ),
				subimage_num( new_subimage_num )
		{
		}
	};

private:
	// Types
	typedef std::vector< t_object_subimage_info > t_object_subimage_info_vector;
	typedef std::vector< t_object_id >            t_shadow_object_id_vector;

public:
	typedef t_object_subimage_info_vector::iterator               t_object_subimage_info_iterator;
	typedef t_object_subimage_info_vector::const_iterator         t_const_object_subimage_info_iterator;
	typedef t_object_subimage_info_vector::reverse_iterator       t_object_subimage_info_reverse_iterator;
	typedef t_object_subimage_info_vector::const_reverse_iterator t_const_object_subimage_info_reverse_iterator;
	typedef t_shadow_object_id_vector::const_iterator			  t_const_shadow_object_id_iterator;

	// Destructor
	virtual ~t_adventure_map_view_cell() {}

	// Member functions
	t_object_subimage_info_iterator				  object_subimage_begin();
	t_const_object_subimage_info_iterator		  object_subimage_begin() const;
	t_object_subimage_info_iterator 			  object_subimage_end();
	t_const_object_subimage_info_iterator 		  object_subimage_end() const;
	int											  object_subimage_count() const;
	t_object_subimage_info_iterator 			  object_subimage_insert( t_object_subimage_info_iterator position,
		                                                                  t_object_subimage_info info );
	t_object_subimage_info_iterator 			  object_subimage_erase( t_object_subimage_info_iterator position );
	t_object_subimage_info_reverse_iterator		  object_subimage_rbegin();
	t_const_object_subimage_info_reverse_iterator object_subimage_rbegin() const;
	t_object_subimage_info_reverse_iterator 	  object_subimage_rend();
	t_const_object_subimage_info_reverse_iterator object_subimage_rend() const;
	t_const_shadow_object_id_iterator			  shadow_begin() const;
	t_const_shadow_object_id_iterator			  shadow_end() const;
	int											  shadow_count() const;
	t_const_shadow_object_id_iterator			  shadow_insert( t_object_id new_shadow_object_id );
	void 										  shadow_erase( t_object_id shadow_object_id );

private:

	// Data members
	t_copy_on_write_ptr< t_object_subimage_info_vector >	m_object_subimage_info_vector_ptr;
	t_copy_on_write_ptr< t_shadow_object_id_vector >		m_shadow_object_id_vector_ptr;
};

inline t_adventure_map_view_cell::t_object_subimage_info_iterator
t_adventure_map_view_cell::object_subimage_begin()
{
	return m_object_subimage_info_vector_ptr->begin();
}

inline t_adventure_map_view_cell::t_const_object_subimage_info_iterator
t_adventure_map_view_cell::object_subimage_begin() const
{
	return m_object_subimage_info_vector_ptr->begin();
}

inline t_adventure_map_view_cell::t_object_subimage_info_iterator
t_adventure_map_view_cell::object_subimage_end()
{
	return m_object_subimage_info_vector_ptr->end();
}

inline t_adventure_map_view_cell::t_const_object_subimage_info_iterator
t_adventure_map_view_cell::object_subimage_end() const
{
	return m_object_subimage_info_vector_ptr->end();
}

inline int t_adventure_map_view_cell::object_subimage_count() const
{
	return m_object_subimage_info_vector_ptr->size();
}

inline t_adventure_map_view_cell::t_object_subimage_info_iterator
t_adventure_map_view_cell::object_subimage_insert(
	t_object_subimage_info_iterator	position,
	t_object_subimage_info			info )
{
	return m_object_subimage_info_vector_ptr->insert( position, info );
}

inline t_adventure_map_view_cell::t_object_subimage_info_iterator
t_adventure_map_view_cell::object_subimage_erase(
	t_object_subimage_info_iterator position )
{
	return m_object_subimage_info_vector_ptr->erase( position );
}

inline t_adventure_map_view_cell::t_object_subimage_info_reverse_iterator
t_adventure_map_view_cell::object_subimage_rbegin()
{
	return m_object_subimage_info_vector_ptr->rbegin();
}

inline t_adventure_map_view_cell::t_const_object_subimage_info_reverse_iterator
t_adventure_map_view_cell::object_subimage_rbegin() const
{
	return m_object_subimage_info_vector_ptr->rbegin();
}

inline t_adventure_map_view_cell::t_object_subimage_info_reverse_iterator
t_adventure_map_view_cell::object_subimage_rend()
{
	return m_object_subimage_info_vector_ptr->rend();
}

inline t_adventure_map_view_cell::t_const_object_subimage_info_reverse_iterator
t_adventure_map_view_cell::object_subimage_rend() const
{
	return m_object_subimage_info_vector_ptr->rend();
}

inline t_adventure_map_view_cell::t_const_shadow_object_id_iterator
t_adventure_map_view_cell::shadow_begin() const
{
	return m_shadow_object_id_vector_ptr->begin();
}

inline t_adventure_map_view_cell::t_const_shadow_object_id_iterator
t_adventure_map_view_cell::shadow_end() const
{
	return m_shadow_object_id_vector_ptr->end();
}

inline int t_adventure_map_view_cell::shadow_count() const
{
	return m_shadow_object_id_vector_ptr->size();
}

// -------------------------------------------------------------------
// abstract map class
// used by both editor and game.  Actual derived classes differ.
// -------------------------------------------------------------------
class t_abstract_adventure_map
{
public:
	// Types
	typedef t_adventure_map_object_id			t_object_id;
	typedef t_abstract_adv_object				t_object;
	typedef t_abstract_adventure_tile			t_tile;
	typedef t_abstract_adventure_tile_vertex	t_tile_vertex;
	typedef t_adventure_map_view_cell			t_view_cell;
	typedef t_quad< t_tile_vertex & >			t_tile_vertex_quad;
	typedef t_quad< t_tile_vertex const & >		t_const_tile_vertex_quad;
	typedef std::map< t_map_point_2d, int >		t_height_map;
	typedef t_vector_set< t_map_point_2d >		t_tile_point_set;
	typedef std::map< t_object_id, int >		t_object_height_delta_set;

	struct t_params
	{
		int		size;
		int		levels;
		long	terrain_random_seed;
	};

	class t_object_floater;

	struct t_apply_height_map_result
	{
		// Data members
		t_tile_point_set			modified_vertex_points;
		t_object_height_delta_set	modified_object_height_deltas;

		// Member functions
		void	swap( t_apply_height_map_result & other );
	};

	// Static data
	enum { k_invalid_object_id = k_invalid_adventure_map_object_id };
	enum { k_view_cell_size = k_adventure_map_view_cell_size };
	enum { k_view_cell_shift = k_adventure_map_view_cell_shift };

	// Constructors/Destructor
	t_abstract_adventure_map( t_abstract_adventure_map const & other );
	explicit t_abstract_adventure_map( t_params const & params );
	t_abstract_adventure_map( int size, int levels, long terrain_random_seed );
	virtual ~t_abstract_adventure_map();

	// Member functions
	t_apply_height_map_result				apply_height_map(
												int						level,
												t_height_map const &	height_map );
	void									apply_height_map(
												t_isometric_vertex_map< int > const & height_map );
	virtual	int								compute_height(
												t_abstract_adv_object const &	object,
												t_level_map_point_2d const &	 pos ) const;
	t_object const &						get_const_object( t_object_id id ) const;
	t_shroud_transition_map const *			get_const_shroud_transition_map_ptr( int team_num ) const;
	t_tile const &							get_const_tile( t_level_map_point_2d point ) const;
	t_tile_vertex const &					get_const_tile_vertex( t_level_map_point_2d point ) const;
	t_const_tile_vertex_quad				get_const_tile_vertex_quad( t_level_map_point_2d point ) const;
	t_view_cell const &						get_const_view_cell(
												int				level,
												t_screen_point	cell_point ) const;
	virtual t_object_id						get_first_object_id() const = 0;
	virtual t_object_id						get_last_object_id() const = 0;
	std::string const &						get_map_description() const;
	t_difficulty							get_map_difficulty() const;
	std::string const &						get_map_name() const;
	t_map_point_2d							get_map_point( t_screen_point point ) const;
	virtual t_object_id						get_next_object_id( t_object_id id ) const = 0;
	int										get_num_levels() const;
	virtual t_object &						get_object( t_object_id id ) = 0;
	virtual t_object const &				get_object( t_object_id id ) const = 0;
	int										get_object_height( t_object_id id ) const;
	virtual t_level_map_point_2d			get_object_pos( t_object_id id ) const = 0;
	t_level_map_point_3d					get_object_pos_3d( t_object_id id ) const;
	virtual t_object_id						get_prev_object_id( t_object_id id ) const = 0;
	int										get_row_start( int row ) const;
	int										get_row_end( int row ) const;
	t_screen_point							get_screen_point( t_map_point_2d point ) const;
	virtual t_shroud_transition_map *		get_shroud_transition_map_ptr( int team_num ) = 0;
	virtual t_shroud_transition_map const *	get_shroud_transition_map_ptr( int team_num ) const = 0;
	int										get_size() const;
	long									get_terrain_random_seed() const;
	virtual t_tile const &					get_tile( t_level_map_point_2d point ) const = 0;
	virtual t_tile &						get_tile( t_level_map_point_2d point ) = 0;
	virtual t_tile_vertex const &			get_tile_vertex( t_level_map_point_2d point ) const = 0;
	virtual t_tile_vertex &					get_tile_vertex( t_level_map_point_2d point ) = 0;
	virtual t_const_tile_vertex_quad		get_tile_vertex_quad( t_level_map_point_2d point ) const;
	virtual t_tile_vertex_quad				get_tile_vertex_quad( t_level_map_point_2d point );
	int										get_vertex_row_start( int row ) const;
	int										get_vertex_row_end( int row ) const;
	t_view_cell const &						get_view_cell( int level, t_screen_point cell_point ) const;
    t_screen_point							get_view_size() const;
	virtual bool							is_floating( t_object_id id ) const = 0;
	bool									is_valid( t_map_point_2d const & point ) const;
	bool									is_valid( t_level_map_point_2d const & point ) const;
	bool									is_valid_view_cell(
												int						level, 
												t_screen_point const &	point ) const;
	bool									is_vertex_valid( t_map_point_2d const & point ) const;
	bool									is_vertex_valid( t_level_map_point_2d const & point ) const;

	// Operators
	t_abstract_adventure_map & operator=( t_abstract_adventure_map const & other );

protected:
	// Constructor
	t_abstract_adventure_map();

	// Member functions
	void			init( t_params const & params );
	void			on_floating_object( t_object_id id );
	void			on_moving_object( t_object_id id );
	void			on_object_moved( t_object_id id );
	void			on_object_placed( t_object_id id );
	void			on_object_sunk( t_object_id id );
	void			on_removing_object( t_object_id id );

	void			set_map_description( std::string const & description );
	void			set_map_difficulty( t_difficulty difficulty );
	void			set_map_name( std::string const & name );

	virtual void	stamp_object( t_object_id id );
	virtual void	unstamp_object( t_object_id id );

private:
	// Types
	class t_impl;

	// Data members
	t_copy_on_write_ptr< t_impl >	m_impl_ptr;

	// Member functions
	t_screen_rect	compute_object_extent( t_object_id id ) const;
	t_screen_rect	compute_object_shadow_extent( t_object_id id ) const;
	int				compute_vertex_light( t_level_map_point_2d const & vertex_point ) const;
	unsigned		get_object_placement_num( t_object_id id ) const;
	t_view_cell &	get_view_cell( int level, t_screen_point cell_point );
	void			stamp_object_image( t_object_id id );
	void			unstamp_object_image( t_object_id id );
};

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_abstract_adventure_map::t_object const &
t_abstract_adventure_map::get_const_object( t_object_id id ) const
{
	return get_object( id );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_abstract_adventure_map::t_view_cell const &
t_abstract_adventure_map::get_const_view_cell(
	int				level,
	t_screen_point	cell_point ) const
{
	return get_view_cell( level, cell_point );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_shroud_transition_map const *
t_abstract_adventure_map::get_const_shroud_transition_map_ptr(
	int	team_num ) const
{
	return get_shroud_transition_map_ptr( team_num );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_abstract_adventure_map::t_tile const &
t_abstract_adventure_map::get_const_tile(
	t_level_map_point_2d point ) const
{
	return get_tile( point );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_abstract_adventure_map::t_tile_vertex const &
t_abstract_adventure_map::get_const_tile_vertex(
	t_level_map_point_2d point ) const
{
	return get_tile_vertex( point );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_abstract_adventure_map::t_const_tile_vertex_quad
t_abstract_adventure_map::get_const_tile_vertex_quad(
	t_level_map_point_2d point ) const
{
	return get_tile_vertex_quad( point );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline t_level_map_point_3d
t_abstract_adventure_map::get_object_pos_3d( t_object_id id ) const
{
	return t_level_map_point_3d( get_object_pos( id ), get_object_height( id ) );
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
inline void t_abstract_adventure_map::t_apply_height_map_result::swap(
	t_apply_height_map_result & other )
{
	modified_vertex_points.swap( other.modified_vertex_points );
	modified_object_height_deltas.swap( other.modified_object_height_deltas );
}

// -------------------------------------------------------------------
// Free functions
// -------------------------------------------------------------------

// -------------------------------------------------------------------
// Compute the height of the specified object if placed at the
// specified location on the specified map.
// -------------------------------------------------------------------
inline t_screen_point get_screen_point(
	t_abstract_adventure_map const &	map,
	t_abstract_adv_object const &		object,
	t_level_map_point_2d const &		pos )
{
	t_screen_point result = map.get_screen_point( pos );

	result.y -= map.compute_height( object, pos );
	return result;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool is_under_fog_of_war(
	t_abstract_adventure_map const &	map,
	int									team_num,
	t_level_map_point_2d const &		object_pos,
	t_map_point_2d const &				footprint_size );

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool is_under_fog_of_war(
	t_abstract_adventure_map const &		map,
	int										team_num,
	t_abstract_adventure_map::t_object_id	object_id );

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool is_under_shroud(
	t_abstract_adventure_map const &	map,
	int									team_num,
	t_level_map_point_2d const &		object_pos,
	t_map_point_2d const &				footprint_size );

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool is_under_shroud(
	t_abstract_adventure_map const &		map,
	int										team_num,
	t_abstract_adventure_map::t_object_id	object_id );

#endif
